/******************************************************************************
 * This file is part of libemb.
 *
 * libemb is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * libemb is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with libemb.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Project: Embedme
 * Author : FergusZeng
 * Email  : cblock@126.com
 * git	  : https://git.oschina.net/cblock/embedme
 * Copyright 2014~2020 @ ShenZhen ,China
*******************************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <iostream>

#include "Tracer.h"
#include "FileUtil.h"
#include "ProcUtil.h"
#include "StrUtil.h"
#include "Socket.h"

using namespace std;
namespace libemb{
/**
 *  \brief  调试信息打印
 *  \param  printLevel 打印级别
 *  \param  format 格式化字串
 *  \return void
 *  \note   none
 */
void Tracer::print(int level,const char* format,...)
{
	if ((m_traceLevel > level) ||			/* 当前级别大于要打印级别 */
		(m_traceLevel > TRACE_LEVEL_REL))	/* 当前级别大于或等于最高可打印级别 */
	{
 		return;
	}
#if 1
	char* buf = m_printBuf.get();
    va_list argp;
    va_start(argp,format);
	int size = TRACE_MAXLEN-1;
    size = vsnprintf(buf,size,format,argp);
	va_end(argp);
    if (size>0) 
    {
        if (size>=TRACE_MAXLEN) /* 超过长度了 */
        {
            buf[TRACE_MAXLEN-5]='.';
            buf[TRACE_MAXLEN-4]='.';
            buf[TRACE_MAXLEN-3]='.';
            buf[TRACE_MAXLEN-2]='\n';
            buf[TRACE_MAXLEN-1]=0;
            size = TRACE_MAXLEN;
        }
        write(fileno(stdout), buf, size);
		if (m_traceFile && m_traceLevel>TRACE_LEVEL_DBG)
		{
			m_traceFile->writeData(buf,size);
		}
		//fflush(stdout);
    }
#else
	#if 1	/* 限制打印长度:TRACE_MAXLEN */
    char* buf = m_printBuf.get();
	int size =0;
    va_list argp;
    va_start(argp,format);
    size = vsnprintf(buf,size,format,argp);
	va_end(argp);
	if (size<0)
	{
		return;
	}
	
	if (size>=TRACE_MAXLEN) 
    {
        buf[TRACE_MAXLEN-5]='.';
        buf[TRACE_MAXLEN-4]='.';
        buf[TRACE_MAXLEN-3]='.';
        buf[TRACE_MAXLEN-2]='\n';
        buf[TRACE_MAXLEN-1]=0;
        size = TRACE_MAXLEN;
    }
   	va_start(argp, format);
	size = vsnprintf(buf, size, format, argp);
	if (size < 0) 
	{
	   return;
	}
	va_end(argp);
	write(fileno(stdout), buf, size);
	if (m_traceFile && m_traceLevel>TRACE_LEVEL_DBG)
	{
		m_traceFile->writeData(buf,size);
	}
	fflush(stdout);
	#else
	char *buf = NULL;
	int size = 0;
	va_list argp;
	va_start(argp, format);
	size = vsnprintf(buf, size, format, argp);
	va_end(argp);
	if (size < 0)
	{
		return;
	}
	size++;
	buf = (char*)malloc(size);
	if (buf==NULL)
	{
		return;
	}
	va_start(argp, format);
	size = vsnprintf(buf, size, format, argp);
	if (size < 0) 
	{
	   free(buf);
	   return;
	}
	va_end(argp);
	write(fileno(stdout), buf, size);
	if (m_traceFile && m_traceLevel>TRACE_LEVEL_DBG)
	{
		m_traceFile->writeData(buf,size);
	}
	//fflush(stdout);
	free(buf);
	#endif
#endif
}

/**
 *  \brief  设置打印级别
 *  \param  level 打印级别
 *  \return void
 *  \note   默认打印级别为TRACE_LEVEL_INFO(不打印DBG信息)
 */
void Tracer::setLevel(int level)
{
	m_traceLevel = CLIP(TRACE_LEVEL_DBG,level,TRACE_LEVEL_REL+1);
}

/**
 *  \brief  返回当前打印级别
 *  \param  void
 *  \return it 当前打印级别
 *  \note   none
 */
int Tracer::getLevel()
{
	return m_traceLevel;
}

bool Tracer::isDebuging()
{
	return (m_traceLevel==TRACE_LEVEL_DBG)?true:false;
}

/**
 *  \brief  以16进制字串的格式打印数据
 *  \param  level 打印级别
 *  \param  tag 标识字串
 *  \param  buf 要打印的内存块地址
 *  \param  len 要打印的内存块长度
 *  \return void
 *  \note   none
 */
void Tracer::printHex(int level,const char* tag,const char* buf,int len)
{
	if ((level < m_traceLevel) ||
        (NULL==tag &&
        NULL==buf) ||
		len <=0)
    {
     	return;
    }
	printf("%s(%02d)-[",tag,len);
	for(auto i=0;i<len;i++)
	{
		printf("%02x ",(unsigned char)(*(buf+i)));
	}
	printf("]\n");
}


bool Tracer::fileOut(bool enable,const char* fileName)
{
	if (enable)
	{
		if (fileName==NULL)
		{
			return false;
		}
		if (m_traceFile)
		{
			m_traceFile->close();
		}
		m_traceFile = std::make_unique<File>();
		if(!m_traceFile->open(fileName, IO_MODE_REWR_ORNEW))
		{
			return false;
		}
	}
	else
	{
		if (m_traceFile)
		{
			m_traceFile->close();
		}
		m_traceFile=nullptr;
	}
	return true;
}

bool Tracer::ttyRemote(const char* remoteMsg, int len, TracerCmdExecutor cmdExecutor)
{
	/* 协议文本“1:4:/dev/pts/x” */
	std::string msg(remoteMsg,len);
    if (msg.size()<14) 
    {
    	return false;
    }
    if (msg.substr(3,10)!=":/dev/pts/")
    {
    	if (msg.substr(3,10)==":>>>cmd<<<" && cmdExecutor!=NULL)
		{
			std::string cmd = msg.substr(13);
			cmdExecutor(CSTR(cmd));
			return true;
    	}
		return false;
	}
    bool enable = !!StrUtil::stringToInt(msg.substr(0,1));
	int level = StrUtil::stringToInt(msg.substr(2,1));
    std::string ptsDev = msg.substr(4);
    if (enable) 
	{
		m_remoteLevel = m_traceLevel;
		setLevel(level);
		#if 0
        close(1);
	    close(2);
	    /* 在新终端打开标准输出 */
	    int rc = (int)open(CSTR(ptsDev), 1);
	    if(rc<0)
	    {
	         TRACE_ERR_CLASS("fileOut stdout to %s error:%s.\n",CSTR(ptsDev), ERRSTR);
	         return false;    
	    }
	    /* 在新终端打开标准错误输出 */
	    rc = (int)open(CSTR(ptsDev), 2);
	    if(rc<0)
	    {
	        TRACE_ERR_CLASS("fileOut stderr to %s error:%s.\n", CSTR(ptsDev), ERRSTR); 
	        return false;
	    }
	    //fflush(NULL);//flush all output streams
		TRACE_REL_CLASS("ctrace setup, level:%d\n",level);
		#endif
	    return true;
    } 
	else 
	{
		setLevel(m_remoteLevel);
		#if 0 /* 在ECU项目中,ctrace退出时会导致SCA亮蓝灯,暂未查找到原因,因此屏蔽此段代码 */
        std::string ttyName = ProcUtil::execute("tty");
	    if (ttyName.empty()) {
	        return false;
	    }
	    ttyName = StrUtil::trimTailBlank(ttyName);
	    /* 在当前终端重新打开标准输出和标准错误输出 */
	    freopen(CSTR(ttyName),"w",stdout);
	    freopen(CSTR(ttyName),"w",stderr);
	    /* 不知道为什么会产生这样一个文件!只能强制把它删除! */
	    ProcUtil::execute("rm -rf not\\ a\\ tty ");
		TRACE_REL_CLASS("ctrace quit, level:%d\n",m_remoteLevel);
		#endif
	    return true;
    }
}
}
